import java.util.*;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import java.io.*;

public class Character implements Entity, Serializable {

	public enum Status {HOSTILE, UNFRIENDLY, NEUTRAL, FRIENDLY, HONOURED, INANIMATE, CORPSE }
	
	Random random = new Random();
	
	protected ArrayList<Ability> Abilities = new ArrayList<Ability>();
	protected ArrayList<Quest> quests = new ArrayList<Quest>();
	protected ArrayList<String> attackMsg = new ArrayList<String>();
	protected ArrayList<StatusEffect> Effects = new ArrayList<StatusEffect>();
	protected String[][] ConversationTopics = new String[10][10];
	
	protected String name;
	protected String orientation;
	protected String displayName;
	protected String description;
	protected GUI gui;
	protected int disposition;
	
	protected boolean canSpeak;
	
	protected boolean isMerchant;
	protected boolean forSale = false;
	
	protected double maxAP;
	
	protected double Weight;
	
	protected int Gold;
	
	protected double ArmourRating;
	
	protected boolean isBed = false;
	
	protected String sex;
	
	protected Location location;
	
	protected double actionPoints;
	
	protected int minAcceptableValue;
	
	protected boolean knowsSecrets;
	
	protected String RejectGift;
	protected String AcceptGift;
	
	protected Status status;
	
	protected ArrayList<Entity> Inventory = new ArrayList<Entity>();
	
	protected int Endurance;
	protected int Strength;
	protected int Intelligence;
	protected int Agility;
	protected int Willpower;
	protected double maxHP;
	protected double currentHP;
	
	protected boolean isRoaming;
	protected boolean isCorpse;
	protected boolean CanBePickedUp = false;
	protected boolean isVisible;
	protected String CantBePickedUp_Why = "You could try, but I doubt they'd let you.";
	int value;
	
	protected String description_base;
	
	protected History history;
	
	protected Weapon Main_Hand;
	protected Armour Head;
	protected Armour Shoulders;
	protected Armour Chest;
	protected Armour Hands;
	protected Armour Legs;
	protected Armour Feet;
	
	
	public Character(String a, String b, String c, String d, int f, History g, GUI h) {
		orientation = a;
		displayName = b;
		name = c;
		description_base = d;
		sex = "He";
		setDescription();
		isRoaming = false;
		isCorpse = false;
		Weight = 70.0;
		Endurance = random.nextInt(3) + 6;
		Strength = random.nextInt(3) + 6;
		Intelligence = random.nextInt(3) + 6;
		Agility = random.nextInt(3) + 6;
		Willpower = random.nextInt(3) + 6;
		maxAP = Endurance * 4;
		maxHP = Endurance * 10;
		currentHP = maxHP;
		isVisible = true;
		disposition = f;
		RejectGift = (name + " rejects your gift");
		AcceptGift = (name + " gladly accepts your gift");
		history = g;
		knowsSecrets = true;
		setStatus("NEUTRAL");
		minAcceptableValue = 100;
		attackMsg.add("With a furious cry, " + name + " launches themselves at you!");
		attackMsg.add(name + " snarls at you and prepares to attack!");
		gui = h;
		Gold = random.nextInt(15);
		isMerchant = false;
		addAbility(new Ability(-10.000000, -10, 1, "Attack"));
		Main_Hand = new Weapon("", "crude", "bone", "fists");
		canSpeak = true;
	}
	
	public boolean canSpeak() {
		return canSpeak;
	}
	
	public void canSpeak(boolean a) {
		canSpeak = a;
	}
	
	public void addTopic(String a, String b) {
		for (int i = 0; i < ConversationTopics.length; i++) {
			if (ConversationTopics[i][0] == null) {
				ConversationTopics[i][0] = a;
				ConversationTopics[i][1] = b + "\n";
				break;
			}
		}
	}
	
	public void findTopic(String a) {
		boolean found = false;
		for (int i = 0; i < ConversationTopics.length; i++) {
			try {
				if (ConversationTopics[i][0].equalsIgnoreCase(a)) {
					gui.getFeedbackWindow().append(name + " says: " + ConversationTopics[i][1]);
					break;
				}
			} catch (NullPointerException e) {
				
					gui.getFeedbackWindow().append(name + " says: I'm sorry, I don't know what you mean by '" + a + "'.");
					break;
				
			}
		}
	}
	
	public void modifyGold(int a) {
		Gold = Gold + a;
	}
	
	public int getGold() {
		return Gold;
	}
	
	public void setGold(int a) {
		Gold = a;
	}
	
	public void Describe(JTextArea y) {
		y.append(description + ".\n\n");
	}
	
	public boolean isMerchant() {
		return isMerchant;
	}
	
	public void setDescription() {
		description = description_base + " ";
		if ((Main_Hand == null) || Main_Hand.getName().equalsIgnoreCase("fists")) {
			description = description + sex + " is unarmed";
		} else {
			description = description + sex + " is carrying a " + Main_Hand.getName();
		}
		if (Chest != null) {
			description = description + ". " + sex + " is clad in a " + Chest.getName();
		} else {
			description = description + ". " + sex + " is bare-chested";
		}
		
		if (Legs != null) {
			description = description + ". " + sex + " is wearing " + Legs.getName();
		} else {
			description = description + ". " + sex + " is bare-legged";
		}
		
		if (Head != null) {
			description = description + ". A " + Head.getName() + " covers the head";
		}
		
		if (Feet != null) {
			if (Head != null) {
				description = description + ", and " + sex + " is wearing " + Feet.getName();
			} else {
				description = description + ". " + sex + " is wearing " + Feet.getName();
			}
		} else {
			if (Head != null) {
				description = description + ". " + sex + " is bare-footed";
			} else {
				description = description + ", and " + sex + " is bare-footed";
			}
			
		}
		
	}
	
	public void startTurn() {
		actionPoints = (actionPoints + (Endurance * 1.5));
	}

	public void addQuest(Quest a) {
		quests.add(a);
		a.setQuestGiver(this);
	}
	
	public Quest getNextQuest() {
		return quests.get(0);
	}
	
	public ArrayList<Quest> getQuests() {
		return quests;
	}
	
	public void printInventory(JTextArea y) {
		y.append("Searching " + displayName + " you find the following items:\n\n");
		for (Entity a : Inventory) {
			y.append(a.getName());
		}
	}
	
	public void Display(JTextArea y) {
		
		if (isCorpse() == false && (currentHP <= 0 )) {
			this.Death();
			y.append(name + " has been slain!");
		} else {
			
		}
		
		if (!status.toString().equalsIgnoreCase("CORPSE")) {
			if (disposition >= -100 && disposition <= 100) {
				setStatus("NEUTRAL");
			} else if (disposition > 100 && disposition <= 500) {
				setStatus("Friendly");
			} else if (disposition > 500) {
				setStatus("Honoured");
			} else if (disposition >= -500 && disposition < -100) {
				setStatus ("unfriendly");
			} else if (disposition < -500) {
				setStatus("Hostile");
			}
		}
		
		y.append(orientation + " is " + displayName + ". ");
		
		
		switch (status) {
		
		case HOSTILE:
			
			y.append("He looks hostile. ");
			
			if (gui.getPlayer().isInCombat() == false && gui.getPlayer().checkCorpse() == false) {
				int chance = random.nextInt(3);
				System.out.println(chance);
				if (chance == 0) {
					y.append("He has not noticed you entering. ");
				} else {
					y.append("He is aware of your presence. ");
					y.append(attackMsg.get(random.nextInt(attackMsg.size())));
					gui.getBattleMan().engage(gui.getPlayer(), this, "NPC");
				}
			} else {
				y.append("You are currently engaged in combat with him. ");
			}

			break;
		
		case UNFRIENDLY:
			y.append(sex + " scowls at you.");
			break;
			
		case NEUTRAL:
			y.append(sex + " nods at you in greeting");
			if (quests.size() > 0) {
				y.append(", and indicates that " + sex +  " would like to speak to you.");
			} else {
				y.append(".");
			}
			break;
			
		case FRIENDLY:
			y.append(sex + " greets you with a friendly wave");
			if (quests.size() > 0) {
				y.append(", and beckons you over.");
			} else {
				y.append(".");
			}
			break;
			
		case HONOURED:
			y.append(sex + " breaks into a big smile");
			if (quests.size() > 0) {
				y.append(", and runs over to speak with you.");
			} else {
				y.append(" when he sees you.");
			}
			break;
			
		case CORPSE:
			y.append("");
			break;
		
		default:
			
			break;
		
		}
	}
	
	public void Use(Player a, String b, JTextArea y, JTextField x) {
		
	}
	
	public void addEntity(Entity a) {
		Inventory.add(a);
		a.setVisible(false);
	}
	
	public ArrayList<Entity> getInventory() {
		return Inventory;
	}
	
	public void removeObject(Entity a) {
		Inventory.remove(a);
	}
	
	public String getOrientation() {
		return orientation;
	}
	
	public void setOrientation(String a) {
		orientation = a;
	}
	
	public String getDisplayName() {
		return displayName;
	}
	
	public void Death() {
			isCorpse = true;
			displayName = "the corpse of " + displayName;
			orientation = "Lying on the floor";
			this.setStatus("CORPSE");
			CantBePickedUp_Why = "The corpse won't fit in your backpack.";
			if (isMerchant == true) {
				location.notShop();
				isMerchant = false;
			}
	}
	
	public String getName() {
		return name;
	}
	
	public String getDescription() {
		return description;
	}
	
	public boolean TestCanBePickedUp() {
		return CanBePickedUp;
	}
	
	public void CantBePickedUp(String b) {
		CantBePickedUp_Why = b;
	}
	
	public String CanBePickedUp_Why() {
		return CantBePickedUp_Why;
	}
	
	public void setStatus(String a) {
		for (Status b : Status.values()) {
			if (b.toString().equalsIgnoreCase(a)) {
				status = b;
			}
		}
	}
	
	public Status getStatus() {
		return status;
	}
	
	public void setContentsVisible(boolean a) {
		for (Entity b : Inventory) {
			b.setVisible(a);
		}
	}
	
	public void setVisible(boolean a) {
		isVisible = a;
	}
	
	public boolean isVisible() {
		return isVisible;
	}
	
	public boolean receiveObject(Entity a) {
		if (a.getValue() < minAcceptableValue) {
			return false;
			
		} else {
			return true;
		}
	}
	
	public int getValue() {
		return value;
	}
	
	public void setValue(int a) {
		value = a;
	}
	
	public void modifyDisposition(int a) {
		disposition = disposition + a;
	}
	
	public void setGiftResponse(String a, String b) {
		RejectGift = a;
		AcceptGift = b;
	}
	
	public String getRejectGiftResponse() {
		return RejectGift;
	}
	
	public String getAcceptGiftResponse() {
		return AcceptGift;
	}
	
	public int getDisposition() {
		return disposition;
	}
	
	public void giveHistory(String a, Location b, JTextArea y) {
		if (a.equalsIgnoreCase("General History")) {
			try {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: " + b.getLocale().getHistory().getGeneralHistory());
			} catch (Exception e) {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: There isn't much history I can give you about this place, to tell you the truth. I myself am just passing through.");
			}
		} else if (a.equalsIgnoreCase("Secret History")) {
			if (knowsSecrets = true) {
				try {
					y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: " + b.getLocale().getHistory().getSecretHistory());
				} catch (Exception e) {
					y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: In all honesty, I don't think there are any secrets about this place, certainly nothing that would be of interest to you.");
				}
			} else {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: Sorry, I don't know any secrets regarding this place.");
			}
		} else if (a.equalsIgnoreCase("General Personal")) {
			try {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: " + this.getHistory().getGeneralHistory());
			} catch (Exception e) {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: Sorry, I really don't have anything to tell you about myself, I am what you see, nothing more.");
			}
		} else if (a.equalsIgnoreCase("Secret Personal")) {
			try {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: " + this.getHistory().getSecretHistory());
			} catch (Exception e) {
				y.append(name.substring(0, 1).toUpperCase() + name.substring(1) + " says: I don't have any secrets to give you, certainly nothing I'd be willing to share!");
			}
		}
	}
	
	public History getHistory() {
		return history;
	}
	
	public void knowsSecrets(boolean a) {
		knowsSecrets = a;
	}
	
	public boolean isCorpse() {
		return isCorpse;
	}
	
	public void modifyCurrentHP(double a) {
		currentHP = currentHP + a;
	}
	
	public double getCurrentHP() {
		return currentHP;
	}
	
	public void minAcceptableValue(int a) {
		minAcceptableValue = a;
	}
	
	public void attack(Player a, JTextArea y, int opponentTurns) {
	
		Ability NPCAttack = this.Abilities.get(random.nextInt(Abilities.size()));
		while (NPCAttack.getBase_Move_Cost() > this.actionPoints || NPCAttack.checkCooldown_NPC(opponentTurns) == true) {
			NPCAttack = this.Abilities.get(random.nextInt(Abilities.size()));
		}

		NPCAttack.FireNPC(a, this, y, opponentTurns);
	}
	
	public double minActionPoints() {
		double minActionPoints = Abilities.get(0).calcMove_Cost(this);
		return Math.abs(minActionPoints);
	}
	
	public double calcFullWeight() {
		return (getArmourWeight() + Main_Hand.getWeight());
	}
	
	public double getArmourWeight() {
		double Armour = 0.0;
		if (Head == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Head.getWeight();
		}
		if (Shoulders == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Shoulders.getWeight();
		}
		if (Chest == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Chest.getWeight();
		}
		if (Hands == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Hands.getWeight();
		}
		if (Legs == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Legs.getWeight();
		}
		if (Feet == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Feet.getWeight();
		}
		return Armour;
	}
	
	public int getStrength() {
		return Strength;
	}
	
	public int getEndurance() {
		return Endurance;
	}
	
	public int getAgility() {
		return Agility;
	}
	
	public int getIntelligence() {
		return Intelligence;
	}
	
	public int getWillpower() {
		return Willpower;
	}
	
	public boolean isLocked() {
		return false;
	}
	
	public Entity getKey() {
		return null;
	}
	
	public void lock() {
		
	}
	
	public void unlock() {
		
	}
	
	public void addAbility(Ability a) {
		Abilities.add(a);
	}
	
	public void modifyActionPoints(double a) {
		actionPoints = actionPoints + a;
	}
	
	public void setActionPoints(double a) {
		actionPoints = a;
	}
	
	public double getActionPoints() {
		return actionPoints;
	}
	
	public Weapon get_Main_Hand() {
		return Main_Hand;
	}
	
	public String getAttackMsg() {
		return attackMsg.get(random.nextInt(attackMsg.size()));
	}
	
	public void setLocation(Location a) {
		location = a;
	}
	
	public Location getLocation() {
		return location;
	}
	
	public void addEffect(StatusEffect a) {
		Effects.add(a);
		this.modifyStrength(a.getPenalty("Strength"));
		this.modifyAgility(a.getPenalty("Agility"));
		this.modifyEndurance(a.getPenalty("Endurance"));
		this.modifyIntelligence(a.getPenalty("Intelligence"));
		this.modifyWillpower(a.getPenalty("Willpower"));
		gui.feedbackWindow.append(name + " is afflicted by " + a.getName() + "!\n");
	}
	
	public void removeEffect(StatusEffect a) {
		Effects.remove(a);
	}
	
	public ArrayList<StatusEffect> getEffects() {
		return Effects;
	}
	
	public void modifyEndurance(int a) {
		Endurance = Endurance + a;
		updateMaxHP();
		updateMaxAP();
	}
	
	public double getWeight() {
		return Weight;
	}
	
	public void modifyStrength(int a) {
		Strength = Strength + a;
	}
	
	public void modifyAgility(int a) {
		Agility = Agility + a;
	}
	
	public void modifyIntelligence(int a) {
		Intelligence = Intelligence + a;
	}
	
	public void modifyWillpower(int a) {
		Willpower = Willpower + a;
	}
	
	public void updateMaxHP() {
		if (currentHP == maxHP) {
			maxHP = Endurance * 10;
			currentHP = Endurance * 10;
		} else {
			maxHP = Endurance * 10;
		}
		if (currentHP > maxHP) {
			currentHP = maxHP;
		}
	}
	
	public boolean isBed() {
		return isBed;
	}
	
	public void isBed(boolean a) {
		isBed = a;
	}
	
	public void equipArmour(Armour a) {
		if (a.getSlot().equalsIgnoreCase("HEAD")) {
			Head = a;
		} else if (a.getSlot().equalsIgnoreCase("SHOULDERS")) {
			Shoulders = a;
		} else if (a.getSlot().equalsIgnoreCase("CHEST")) {
			Chest = a;
		} else if (a.getSlot().equalsIgnoreCase("HANDS")) {
			Hands = a;
		} else if (a.getSlot().equalsIgnoreCase("LEGS")) {
			Legs = a;
		} else if (a.getSlot().equalsIgnoreCase("FEET")) {
				Feet = a;
		}
		modifyEndurance(a.getBonus("Endurance"));
		modifyStrength(a.getBonus("Strength"));
		modifyAgility(a.getBonus("Agility"));
		modifyIntelligence(a.getBonus("Intelligence"));
		modifyWillpower(a.getBonus("Willpower"));
		calcArmour();
		setDescription();
	}
	
	public double getArmourRating() {
		double result = ((100 - ArmourRating) / 100);
		return result;
	}
	
	public void equipArmours(Armour ... armours) {
		for (Armour a : armours) {
			equipArmour(a);
		}
		calcArmour();
		setDescription();
	}
	
	public void calcArmour() {
		double Armour = 0.0;
		if (Head == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Head.getArmourValue();
		}
		if (Shoulders == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Shoulders.getArmourValue();
		}
		if (Chest == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Chest.getArmourValue();
		}
		if (Hands == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Hands.getArmourValue();
		}
		if (Legs == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Legs.getArmourValue();
		}
		if (Feet == null) {
			Armour = Armour + 0.0;
		} else {
			Armour = Armour + Feet.getArmourValue();
		}
		
		ArmourRating = Armour * 1.4;
	}
	
	public void equipWeapon(Weapon a) {
		Main_Hand = a;
		setDescription();
	}
	
	public double getMaxAP() {
		return maxAP;
	}
	
	public void updateMaxAP() {
		maxAP = Endurance * 4;
	}
	
	public void isMerchant(boolean a) {
		isMerchant = a;
		if (isMerchant == true) {
			for (Entity item : Inventory) {
				if (item.getValue() > 0) {
					item.forSale(true);
				}
			}
		} else if (isMerchant == false) {
			for (Entity item : Inventory) {
				item.forSale(false);
			}
		}
	}
	
	public int countSaleItems() {
		int counter = 0;
		for (Entity a : Inventory) {
			if (a.isForSale() == true) {
				counter++;
			}
		}
		return counter;
	}
	
	public void forSale(boolean a) {
		forSale = a;
	}
	
	public boolean isForSale() {
		return forSale;
	}
	
}
